﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Otonomos.Utils;

namespace Otonomos.World
{
    public class SimpleWall2D : Object2D
    {
        public SimpleWall2D(Point2D firstPoint, Point2D secondPoint, WallType wallType)
        {
            FirstPoint = firstPoint;
            SecondPoint = secondPoint;
            WallType = wallType;
        }

        public double Length
        {
            get
            {
                return FirstPoint.Distance(SecondPoint);
            }
        }

        public Point2D FirstPoint { get; private set; }

        public Point2D SecondPoint { get; private set; }

        public WallType WallType { get; private set; }

        public override double Distance(Point2D point)
        {
            var dirX = SecondPoint.X - FirstPoint.X;
            var dirY = SecondPoint.Y - FirstPoint.Y;

            // s tells us how often we have to add the vector (-dirY/dirX) - the orthogonal vector to this wall - to the parameter point
            // to reach the closest point on an infinitely long wall to the parameter point
            var s = (FirstPoint.Y * dirX + point.X * dirY - FirstPoint.X * dirY - point.Y * dirX) / (dirX * dirX + dirY * dirY);
            
            if (s < 0 && WallType == WallType.SingleRight || s > 0 && WallType == WallType.SingleLeft)
            { // the point is on the wrong side of this wall
                return double.PositiveInfinity;
            }

            var intersectPoint = new Point2D(point.X - s * dirY, point.Y + s * dirX);
            if (intersectPoint.X < FirstPoint.X && intersectPoint.X < SecondPoint.X ||
                intersectPoint.X > FirstPoint.X && intersectPoint.X > SecondPoint.X ||
                intersectPoint.Y < FirstPoint.Y && intersectPoint.Y < SecondPoint.Y ||
                intersectPoint.Y > FirstPoint.Y && intersectPoint.Y > SecondPoint.Y)
            { // the intersection point is not on the wall
                if (FirstPoint.Distance(intersectPoint) < SecondPoint.Distance(intersectPoint))
                {
                    intersectPoint = FirstPoint;
                }
                else
                {
                    intersectPoint = SecondPoint;
                }
            }
            var result = point.Distance(intersectPoint);
            return result;
        }

        public override string ToString()
        {
            return string.Format("{0}-{1}[{2}]", FirstPoint, SecondPoint, WallType);
        }

        public override bool Equals(object obj)
        {
            return this.Equals(obj as SimpleWall2D);
        }

        public bool Equals(SimpleWall2D other)
        {
            if (other == null)
                return false;
            return other.FirstPoint == FirstPoint && other.SecondPoint == SecondPoint && other.WallType == WallType;
        }

        public int CompareTo(SimpleWall2D other)
        {
            if (other.FirstPoint != FirstPoint)
            {
                return FirstPoint.CompareTo(other.FirstPoint);
            }

            if (other.SecondPoint != SecondPoint)
            {
                return SecondPoint.CompareTo(other.SecondPoint);
            }

            return WallType.CompareTo(other.WallType);
        }

        public static bool operator ==(SimpleWall2D a, SimpleWall2D b)
        {
            return EqualsUtility.OperatorEquals(a, b);
        }

        public static bool operator !=(SimpleWall2D a, SimpleWall2D b)
        {
            return !EqualsUtility.OperatorEquals(a, b);
        }

        public override int GetHashCode()
        {
            return FirstPoint.GetHashCode() ^ SecondPoint.GetHashCode() ^ WallType.GetHashCode();
        }
    }
}
